/*
modbus_485.c
package modbus 485 function for x project
designed by lunanting

-------------------------
2020-03-24 11:06:56
create file
-------------------------
*/
#include "modbus_485.h"
#include "cli_cmd.h"

static MB_state mb3_state={0},mb1_state={0},mb2_state={0};

MB_state *xp_get_modbus_port(uint8_t port);

/*
get modbus buzy state
param:
    port:inquire bus port
return:0=free,1=buzy
*/
MB_buzy_state xp_get_modbus_buzy(uint8_t port){
    MB_state *state=xp_get_modbus_port(port);
    return state->buzy_state;
}



/*
get modbus port state
param:
    port:modbus uart port
return:MB_state pointer
*/
MB_state *xp_get_modbus_port(uint8_t port){
    MB_state *state;
    switch (port)
    {
    case 1:
        state=&mb1_state;
        break;
    case 2:
        state=&mb2_state;
        break;
    case 3:
        state=&mb3_state;
        break;
    //add other modbus port


    //end
    default:
        println("error:xp_get_modbus_port() port=%d,undefined!",port);
        state=NULL;
        break;
    }
    return state;
}

/*
modbus read data
param:
    fun:modbus function type
    slave_id: slave device id
    start_addr:read start address/register address
    read_len:read length
    resp_buf:save slave device response read data buffer
    resp_len:save slave device response data length
return:
    0=success
    -1:NULL pointer input
    -2:fun type error
*/
int xp_modbus_read(uint8_t port,const MB_fun_type fun,const uint8_t slave_id,const uint16_t start_addr,const uint16_t read_len,uint8_t *resp_buf,uint16_t *resp_len){
    if(resp_buf==NULL||resp_len==NULL){
        println("xp_modbus_read() NULL pointer detected!");
        return -1;
    }
    if(fun!=mb_hold_r&&fun!=mb_coil_r){
        println("xp_modbus_read() fun param input error ,not a read type!");
        return -2;
    }

    MB_data data;

    data.mb_fun=fun;
    data.slave_id=slave_id;
    data.start_addr=start_addr;
    data.rwLen=read_len;
    data.resBuf=resp_buf;
    data.resLen=resp_len;

    return xp_modbus_ask(port,&data);
}


/*
modbus write data
param:
    fun:modbus function type
    slave_id: slave device id
    start_addr:read start address/register address
    read_len:read length
    resp_buf:save slave device response read data buffer
    resp_len:save slave device response data length
return:
    0=success
    -1:NULL pointer input
    -2:fun type error
    -3:write length big than max value defined
*/
int xp_modbus_write(uint8_t port,const MB_fun_type fun,const uint8_t slave_id,const uint16_t start_addr,uint16_t wLen,const uint16_t *wBuf,uint8_t *resBuf,uint16_t *resLen){
    MB_state *state;

    
    if(resBuf==NULL||resLen==NULL){
        println("error:xp_modbus_write() NULL pointer detected!");
        return -1;
    }
    if(fun!=mb_hold_w&&fun!=mb_coil_w){
        println("error:xp_modbus_write() fun param input error ,not a write type!");
        return -2;
    }
    if((state=xp_get_modbus_port(port))==NULL){
        println("eror:xp_modbus_write() geted state=NULL ! code=%d",-10);
        return -10;
    }

    MB_data data;
    

    if(wLen>MD_WRITEBUF_SIZE){
        wLen=MD_WRITEBUF_SIZE;
        println("warning:xp_modbus_write() wLen is bigger than MD_WRITEBUF_SIZE!");
        return -3;
    }

    data.mb_fun=fun;
    data.slave_id=slave_id;
    data.start_addr=start_addr;
    data.rwLen=wLen;
    data.resBuf=resBuf;
    data.resLen=resLen;
    data.wBuf=state->write_buf;

    memcpy(data.wBuf,wBuf,wLen*2);

    return xp_modbus_ask(port,&data);
}


/*
modbus read with MB_data
param:
    data:MB_data type data
return:
    0=success
    -1:NULL pointer input
    -2:fun type error
*/
int xp_modbus_read_MB(uint8_t port,MB_data *data){
    return xp_modbus_read(port,data->mb_fun,data->slave_id,data->start_addr,data->rwLen,data->resBuf,data->resLen);
}


/*
modbus write with MB_data
param:
    data:MB_data type data
return:
    0=success
    -1:NULL pointer input
    -2:fun type error
    -3:write length big than max value defined
*/
int xp_modbus_write_MB(uint8_t port,MB_data *data){
    return xp_modbus_write(port,data->mb_fun,data->slave_id,data->start_addr,data->rwLen,data->wBuf,data->resBuf,data->resLen);
}


/*
init a modbus on a uart
param:
    port: uart port
    speed:uart speed
    check:uart parity type
return:
    0=success

*/
int xp_modbus_init(const uint8_t port,const int speed,const mb_parity_t parity,const int  wait_time){
    MB_state *state=xp_get_modbus_port(port);
    int ret=0;
    if(state==NULL){
        println("error: xp_modbus_init() geted state=NULL,port=%d,code=%d!",port,-1);
        return -1;
    }
    if(state->init==1){
        if(state->speed!=speed){
            println("error:modbus port inited,and speed is different!,bus speed=%d,set speed=%d",state->speed,speed);
            return -20;
        }
        return 0;
    }
    state->init=0;
    state->port=port;
    state->speed=speed;
    state->parity=parity;
    state->wait_time=wait_time;
    state->buzy_state=0;
    ret=aos_sem_new(&state->sem,1);
    if(ret!=0){
        println("error:xp_modbus_init() create a sem fail ! code=%d",ret);
        return ret;
    }
    ret=aos_mbmaster_rtu_init(&state->mb_handler,port,speed,parity);
    if(ret==0){
		println("modbus init success!uart port=%d",port);
        state->init=1;
	}
	else{
        println("error:xp_modbus_init() rtu init fail ! code=%d",ret);
        return ret;
    }
    return 0;
}


/*
modbus port deinit
param:
    port:modbus uart port
return:
    0=success

*/
int xp_modbus_Deinit(uint8_t port){
    MB_state *state=xp_get_modbus_port(port);
    int ret=0;

    if(state==NULL){
        println("error: xp_modbus_Deinit() geted state=NULL,port=%d,code=%d!",port,-1);
        return -1;
    }
    ret=aos_mbmaster_rtu_uninit(state->mb_handler);
    state->mb_handler=NULL;
    return ret;
}


/*
modbus master ask slave device
param:
    data:operation modbus data
return:
    0=success
    other=fail
*/
int xp_modbus_ask(uint8_t port,MB_data *data){
    int ret=0;
    int wait=0;

    if(data==NULL){
        println("error:xp_modbus_ask() data pointer=NULL!");
        return -10;
    }
    MB_state *state=xp_get_modbus_port(port);
    if(state->mb_handler==NULL){
        println("error:xp_modbus_ask() state->mb_handler=NULL!,port=%d",port);
        return-11;
    }
    wait=aos_sem_wait(&state->sem,state->wait_time);
    if(wait!=0){
        // state->buzy_state=0;
        // aos_sem_signal(&state->sem);
        return wait;
    }
    state->buzy_state=1;
    memcpy(&state->data,data,sizeof(MB_data));
    aos_msleep(4);       //按照9600速率等待
    *(data->resLen)=0;
    ret=xp_modbus_transfer(state);
    state->buzy_state=0;
    aos_sem_signal(&state->sem);
    //if (ret != 0) return -1;
    if(*(data->resLen) == 0){
        return -1;
    }
    return 0;
 }


/*
modbus exchange data
param:
    state:MB_state type data
return:
    0=success
    -10:state=NULL
    -11:state->mb_handler=NULL

*/
int xp_modbus_transfer(MB_state *state){
    int ret=0;

    if(state==NULL){
        println("error:xp_modbus_transfer() state=NULL!");
        return -10;
    }
    if(state->mb_handler==NULL){
        println("error:xp_modbus_transfer() state->mb_handler=NULL!");
        return -11;
    }
    // print("====>modbus send:fun=%d,id=%d,addr=0x%x,len=%d",state->data.mb_fun,
    //                                                         state->data.slave_id,
    //                                                         state->data.start_addr,
    //                                                         state->data.rwLen);
    switch (state->data.mb_fun)
    {
    case mb_hold_w: 
		// print(",val=%d\r\n",state->data.wBuf[0]);
        ret=aos_mbmaster_write_holding_reginster(state->mb_handler,state->data.slave_id,state->data.start_addr,
                                                    state->data.rwLen,(int16_t *)state->data.wBuf,state->data.resBuf,(uint8_t *)state->data.resLen);
        break;
    case mb_hold_r:
		// print(" \r\n");
        ret=aos_mbmaster_read_holding_reginster(state->mb_handler,state->data.slave_id,state->data.start_addr,
                                                state->data.rwLen,state->data.resBuf,(uint8_t *)state->data.resLen);
        break;
    case mb_coil_w:
		// print(",val=%d\r\n",state->data.wBuf[0]);
        ret=aos_mbmaster_write_coil_reginster(state->mb_handler,state->data.slave_id,state->data.start_addr,
                                                state->data.wBuf[0],state->data.resBuf,(uint8_t *)state->data.resLen);
        break;
    case mb_coil_r:
		// print(" \r\n");
        ret=aos_mbmaster_read_coil_reginster(state->mb_handler,state->data.slave_id,state->data.start_addr,
                                                state->data.rwLen,state->data.resBuf,(uint8_t *)state->data.resLen);
        break;
    case mb_single_w:
		// print(",val=%d\r\n",state->data.wBuf[0]);
        ret=aos_mbmaster_single_write_reginster(state->mb_handler,state->data.slave_id,state->data.start_addr,
                                        state->data.wBuf,state->data.rwLen,state->data.resBuf,state->data.resLen);
        break;
    case mb_single_r:
		// print(" \r\n");
        ret=aos_mbmaster_single_read_reginster(state->mb_handler,state->data.slave_id,state->data.start_addr,
                                        state->data.rwLen,state->data.resBuf,state->data.resLen);
        break;
    default:
        break;
    }
    return ret;
}


/*
modbus test
*/
void xp_modbus_test(uint8_t id,uint16_t addr,uint16_t val){
    MB_data data;
    uint8_t rec_buf[10];
    uint8_t rec_len;
    int ret=0;

    data.resBuf=rec_buf;
    data.resLen=&rec_len;

    data.mb_fun=mb_hold_w;
    data.slave_id=id;
    data.start_addr=addr;
    data.rwLen=1;
    data.wBuf=&val;

    ret=xp_modbus_ask(2,&data);
    if(ret==0){
        println("modbus response data=%d,%d,%d,%d,len=%d",rec_buf[0],
                                                            rec_buf[1],
                                                            rec_buf[2],
                                                            rec_buf[3],
                                                            rec_len);
    }
    else{
        println("error:modbus test response fail!,code=%d",ret);
    }
}

//end of the file
